#pragma once
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include "Log.hpp"
#include "Util.hpp"
#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_400 "400.html"
#define PAGE_404 "404.html"
#define PAGE_500 "500.html"

#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500

static std::string Code2Desc(int code)
{
    std::string desc;
    switch(code)
    {
        case 200:
            desc = "OK";
            break;
        case 404:
            desc = "Not Found";
            break;
        default:
            break;
    }
    return desc;
}

//根据文件后缀名返回MIME类型, 用于HTTP响应头
static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"},
    };

    auto iter = suffix2desc.find(suffix);
    if(iter != suffix2desc.end()){
        return iter->second;
    }
    return "text/html";
}

class HttpRequest{
    public:
        std::string request_line;                   //请求行
        std::vector<std::string> request_header;    //请求报头
        std::string blank;                          //空行
        std::string request_body;                   //正文

        //解析完毕之后的结果
        std::string method;                         //请求方法
        std::string uri;                            //请求资源
        std::string version;                        //HTTP版本

        std::unordered_map<std::string, std::string> header_kv;     
        int content_length = 0;                     //正文的字节数
        std::string path;                           //url请求资源的路径
        std::string suffix;                         //后缀
        std::string query_string;                   //url的参数(GET)

        bool cgi = false;                           //是否cgi调用
        int size;                                   //请求资源的大小
};

class HttpResponse{
    public:
        std::string status_line;                    //响应行
        std::vector<std::string> response_header;   //响应报头
        std::string blank = LINE_END;               //空行
        std::string response_body;                  //响应正文

        int status_code = OK;                       //状态码
        int fd = -1;                                //记录申请资源的fd
};

//读取请求，分析请求，构建响应
class EndPoint{
    private:
        int sock;                                   //客户端连接
        HttpRequest http_request;
        HttpResponse http_response;
        bool stop;                                  //是否读取完毕
    private:
        //1.读取并分析请求
        bool RecvHttpRequestLine();             //读取请求行
        bool RecvHttpRequestHeader();           //读取请求报头
        void ParseHttpRequestLine();            //分析请求行
        void ParseHttpRequestHeader();          //分析报头
        bool IsNeedRecvHttpRequestBody();       //判断是否读取请求正文
        bool RecvHttpRequestBody();             //读取请求正文

        //2.设计CGI->获取对应的"响应正文"
        int ProcessCgi();
        int ProcessNonCgi();                    
 
        //3.错误处理
        void HandlerError(std::string page);    //返回404页面的响应

        //4.构建响应与发送响应
        void BuildOkResponse();                 //正常的响应
        void BuildHttpResponseHelper();         //根据不同的code构建不同的响应

    public:
        EndPoint(int _sock):sock(_sock), stop(false)
        {}
        bool IsStop(){return stop;}

        //1.读取并分析请求
        void RecvHttpRequest()
        {
            if( (!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()) )
            {
                ParseHttpRequestLine();
                ParseHttpRequestHeader();
                RecvHttpRequestBody();
            }
        }

        //2.构建响应与发送响应
        //构建响应
        void BuildHttpResponse()
        {
            std::string _path;
            struct stat st;
            std::size_t found = 0;                      //表示文件状态, 用于获取文件的各种属性
            auto &code = http_response.status_code;

            //1.处理请求
            //处理非法请求
            if(http_request.method != "GET" && http_request.method != "POST")
            {
                std::cout << "method: " << http_request.method << std::endl;
                LOG(WARNING, "method is not right");
                code = BAD_REQUEST;
                goto END;
            }
            //处理GET请求
            if(http_request.method == "GET")
            {   
                //处理GET带参
                size_t pos = http_request.uri.find('?');
                if(pos != std::string::npos)
                {
                    Util::CutString(http_request.uri, http_request.path, http_request.query_string, "?");
                    http_request.cgi = true;
                }
                else
                {
                    http_request.path = http_request.uri;
                }
            }
            //处理POST请求
            else if(http_request.method == "POST")
            {
                http_request.cgi = true;
                http_request.path = http_request.uri;
            }

            //2.定位资源路径
            _path = http_request.path;
            http_request.path = WEB_ROOT;
            http_request.path += _path;
            if(http_request.path[http_request.path.size()-1] == '/')//特殊处理webroot/
            {
                http_request.path += HOME_PAGE;//默认给首页
            }

            //3.判断资源是否存在
            if(stat(http_request.path.c_str(), &st) == 0)//拿到资源文件
            {   //资源存在
                if(S_ISDIR(st.st_mode))
                {
                    //请求的资源是目录, 特殊处理->返回该目录下的默认资源:index.html
                    http_request.path += "/";
                    http_request.path += HOME_PAGE;
                    stat(http_request.path.c_str(), &st);
                }
                if( (st.st_mode&S_IXUSR) || (st.st_mode&S_IXGRP) || (st.st_mode&S_IXOTH) )
                {
                    //是可执行程序, 执行CGI
                    http_request.cgi = true;
                }
                http_request.size = st.st_size;
            }
            else
            {   //资源不存在
                LOG(WARNING, http_request.path + " Not Found");
                code = NOT_FOUND;
                goto END;
            }

            //拿到后缀->构建响应头预备(Content-Type)
            found = http_request.path.rfind(".");
            if(found == std::string::npos)
            {
                http_request.suffix = ".html";
            }
            else
            {
                http_request.suffix = http_request.path.substr(found);
            }

            if(http_request.cgi)
            {
                code = ProcessCgi();    //执行目标程序，拿到结果:http_response.response_body;
            }
            else
            {
                code = ProcessNonCgi(); //简单的网页返回，返回静态网页,只需要打开即可
            }

END:
            BuildHttpResponseHelper();  //开始构建响应: 响应行, 响应头, 空行, 正文
        }

        //发送响应
        void SendHttpResponse()
        {   //1.发送响应行
            send(sock, http_response.status_line.c_str(), http_response.status_line.size(), 0);
            //2.发送响应报头
            for(auto iter : http_response.response_header)
            {
                send(sock, iter.c_str(), iter.size(), 0);
            }
            //3.发送空行
            send(sock, http_response.blank.c_str(), http_response.blank.size(), 0);
            //4.发送响应正文
            if(http_request.cgi)//cgi调用
            {
                auto &response_body = http_response.response_body;
                size_t size = 0;
                size_t total = 0;
                const char *start = response_body.c_str();
                while( total < response_body.size() && (size = send(sock, start + total, response_body.size() - total, 0)) > 0)
                {
                    total += size;
                }
            }
            else//非cgi: 可能是某种静态资源
            {
                sendfile(sock, http_response.fd, nullptr, http_request.size);//将对应的文件内容发送给客户端
                close(http_response.fd);
            }
        }
        ~EndPoint()
        {
            close(sock);
        }
};


class CallBack{
    public:
        CallBack()
        {}
        void operator()(int sock)
        {
            HandlerRequest(sock);
        }

        void HandlerRequest(int sock)
        {
            LOG(INFO, "Hander Request Begin");
#ifdef DEBUG
            //For Test
            char buffer[4096];
            recv(sock, buffer, sizeof(buffer), 0);
            std::cout << "-------------begin----------------" << std::endl;
            std::cout << buffer << std::endl;
            std::cout << "-------------end----------------" << std::endl;
#else 
            EndPoint *ep = new EndPoint(sock);
            ep->RecvHttpRequest();
            if(!ep->IsStop())
            { 
                LOG(INFO, "Recv No Error, Begin Build And Send");
                ep->BuildHttpResponse();
                ep->SendHttpResponse();
            }
            else
            {
                LOG(WARNING, "Recv Error, Stop Build And Send");
            }
            delete ep;
#endif
            LOG(INFO, "Hander Request End");
        }
        ~CallBack()
        {}
};
